package com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema;

import java.util.ArrayList;
import java.util.List;

import com.db4o.internal.odbgen.plugins.java.maingenerator.XmlSchemaElement;
import com.db4o.odbgen.OdbgenException;
import com.db4o.odbgen.plugins.xmlschema.Element;
import com.db4o.odbgen.plugins.xmlschema.XmlSchema;

/**
 * 
 * Refines an already existing {@link TypedXmlSchema} by
 * adding methods.
 * After this step, a {@link TypedXmlSchema} must be ready to be written on
 * disk without any errors.
 * @author liviug
 */
class TypedXmlSchemaGeneratorStep2{

    private TypedXmlSchema _typedXmlSchema;
    private List<String> _globalImports;

    /**
     * Constructs a new generator.
     * @param typedXmlSchema The {@link TypedXmlSchema} to generate.
     * @param globalImports A comma separated list of java imports. 
     * This list will be appended to each class, enum, interface.
     * @param standardTemplatesJarName The name of the standard templates jar.
     */
    public TypedXmlSchemaGeneratorStep2(
            TypedXmlSchema typedXmlSchema, 
            String globalImports
            ) {
        this._typedXmlSchema = typedXmlSchema;
        
        
        //Initialize global imports.
        this._globalImports = new ArrayList<String>();
        String[] globalImportsArray = globalImports.split(",");
        for(String globalImport : globalImportsArray){
            if(!globalImport.isEmpty()){
                this._globalImports.add(globalImport);
            }
        }
    }

    public void generate() throws OdbgenException {
        //Create classes.
        for(TSClass c : this._typedXmlSchema.getClasses()){
            this.generateClass(c);
        }

        //Create interfaces.
        for(TSInterface i : this._typedXmlSchema.getInterfaces()){
            this.generateInterface(i);
        }

        //Create enums.
        for(TSEnum e : this._typedXmlSchema.getEnumerations()){
            this.generateEnum(e);
        }
    }

    /**
     * Adds additional elements to an already existing class.
     * Currently it adds
     * <li>add imports
     * <li>add getter/setters according to existing fields
     * <li>implement any interface
     * @param tsInterface The interface to add the elements.
     * @throws OdbgenException 
     */
    private void generateClass(TSClass tsClass) throws OdbgenException{
        //Imports.
        List<String> imports = new ArrayList<String>();
        imports.addAll(this._globalImports);
        imports.addAll(this.buildImports(tsClass.getElement()));

        tsClass.getImports().addAll(imports);
        

        //Methods
        List<TSMethod> methods = new ArrayList<TSMethod>();

        //Getters
        for(TSField field : tsClass.getFields()){
            TSMethod getter = this._typedXmlSchema.buildGetterMethod(
                    tsClass, 
                    field, 
                    TSModifier.getPublicModifier(), 
                    this._typedXmlSchema.buildGetterName(field)
                    ); 
            methods.add(getter);
        }

        //Setters
        for(TSField field : tsClass.getFields()){
            TSMethod setter = this._typedXmlSchema.buildSetterMethod(
                    tsClass, 
                    field, 
                    TSModifier.getPublicModifier(), 
                    this._typedXmlSchema.buildSetterName(field)
                    ); 
            methods.add(setter);
        }

        tsClass.getMethods().addAll(methods);

        //Implement all interfaces for the given class.
        for(TSInterface itf : tsClass.getInterfaces()){
            this.implementInterface(tsClass, itf);
        }
        
    }



    /**
     * Given a {@link TSClass}, implements the given {@link TSInterface}.
     * @param tsClass The class to implement all interface for.
     * @param tsInterface The interface that must be implemented. 
     * @throws OdbgenException 
     */
    private void implementInterface(
            TSClass tsClass,
            TSInterface tsInterface
            ) throws OdbgenException{
        //Iterage all getter/setters and add necessary fields and methods.
        for(TSGetterSetter gs : tsInterface.getGetterSetters()){
            if(gs.hasGetter() || gs.hasSetter()){
                TSField field = new TSField(
                        null, 
                        TSModifier.getPrivateModifier(),
                        gs.getBaseName(), 
                        gs.getType()
                        );
                tsClass.getFields().add(field);

                if(gs.hasGetter()){
                    TSMethod getter = this._typedXmlSchema.buildGetterMethod(
                            tsClass, 
                            field, 
                            TSModifier.getPublicModifier(), 
                            this._typedXmlSchema.buildGetterName(field)
                            ); 
                    tsClass.getMethods().add(getter);
                }
                
                if(gs.hasSetter()){
                    TSMethod setter = this._typedXmlSchema.buildSetterMethod(
                            tsClass, 
                            field, 
                            TSModifier.getPublicModifier(), 
                            this._typedXmlSchema.buildSetterName(field)
                            ); 
                    tsClass.getMethods().add(setter);
                }
            }
        }
    }

    /**
     * Builds a list of imports given an {@link XmlSchema} element.
     * @param elem The element used to build the package. This must represent a type declaration.
     */
    private List<String> buildImports(Element elem) throws OdbgenException {
        List<String> res = new ArrayList<String>();
        for(Element imports : elem.getChildren(XmlSchemaElement.IMPORT.getName())){
            String importValue = imports.getSingleChild(XmlSchemaElement.IMPORTVALUE.getName()).getContent();
            String[] importsArray = importValue.split(",");
            for(String imp : importsArray){
                if(!imp.isEmpty()){
                    res.add(imp);
                }
            }
        }
        return res;
    }

    /**
     * Adds additional elements to an already existing interface.
     * Currently it adds only imports.
     * @param tsInterface The interface to add the elements.
     * @throws OdbgenException 
     */
    private void generateInterface(TSInterface tsInterface) throws OdbgenException {

        //Imports.
        List<String> imports = new ArrayList<String>();
        imports.addAll(this._globalImports);
        imports.addAll(this.buildImports(tsInterface.getElement()));
        
        tsInterface.getImports().addAll(imports);
        
    }   
    
    /**
     * Adds additional elements to an already existing enum.
     * Currently it adds only imports.
     * @param tsEnum The enum to add the elements.
     * @throws OdbgenException 
     */
    private void generateEnum(TSEnum tsEnum) throws OdbgenException {

        //Imports.
        List<String> imports = new ArrayList<String>();
        imports.addAll(this._globalImports);
        imports.addAll(this.buildImports(tsEnum.getElement()));
        
        tsEnum.getImports().addAll(imports);
    }   
    
    

}
